﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.ComponentModel;

using SharedGenomics.Core;

namespace SharedGenomics.Workbench.Models
{
    public class DataGridSource<T> : BindingList<T>
    {
        public DataGridSource()
            : base()
        {
            this.AllItems = new List<T>();
        }

        private Func<T, bool> _filter;

        public virtual void LoadItems(IEnumerable<T> items)
        {
            this.AllItems.Clear();
            this.AllItems.AddRange(items ?? Enumerable.Empty<T>());

            this.ItemsChanged.Raise(this);

            this.ApplyFilter();
        }

        public void AddPropertySort(string propertyName, Comparison<T> comparison)
        {
            throw new NotImplementedException();
        }

        public Func<T, bool> Filter
        {
            get { return this._filter; }
            set
            {
                this._filter = value;
                this.ApplyFilter();
            }
        }

        protected void ApplyFilter()
        {
            //clear item list and add all items that pass the filter (if one exists)
            this.Items.Clear();

            Func<T, bool> identityFilter = i => true;
            foreach (T item in this.AllItems.Where(this._filter ?? identityFilter))
            {
                this.Items.Add(item);
            }

            this.OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
        }

        protected override void ClearItems()
        {
            this.AllItems.Clear();
            this.ItemsChanged.Raise(this);

            base.ClearItems();
        }

        protected override void ApplySortCore(PropertyDescriptor prop, ListSortDirection direction)
        {
            List<T> items = this.Items as List<T>;
            // Apply and set the sort, if items to sort
            if (items != null)
            {
                PropertyDescriptorComparer pc = new PropertyDescriptorComparer(prop, direction);
                items.Sort(pc);
                this.IsItemListSorted = true;
            }
            else
            {
                this.IsItemListSorted = false;
            }

            // Let bound controls know they should refresh their views
            this.OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
        }

        protected override int FindCore(PropertyDescriptor prop, object key)
        {
            return this.FindByProperty(prop, key);
        }

        private int FindByProperty(PropertyDescriptor prop, object key)
        {
            PropertyInfo pi = typeof(T).GetProperty(prop.Name);

            for (int i = 0; i < this.Items.Count; i++)
            {
                //get the value for the given property for the current result
                T current = this.Items[i];
                object val = pi.GetValue(current, null);

                //if the values are equal the object has been found
                if (Object.Equals(val, key))
                    return i;
            }

            //item not found
            return -1;
        }

        protected override bool SupportsSearchingCore
        {
            get { return true; }
        }

        protected override bool SupportsSortingCore
        {
            get { return true; }
        }

        protected override bool IsSortedCore
        {
            get { return this.IsItemListSorted; }
        }

        protected override void RemoveSortCore()
        {
            this.IsItemListSorted = false;
        }

        private bool IsItemListSorted { get; set; }
        protected List<T> AllItems { get; private set; }

        public event EventHandler ItemsChanged;

        #region SnpAnalysisPropertyComparer

        private class PropertyDescriptorComparer : IComparer<T>
        {
            private PropertyDescriptor _propertyDescriptor;
            private ListSortDirection _sortDirection;

            public PropertyDescriptorComparer(PropertyDescriptor propDecriptor, ListSortDirection sortDirection)
            {
                this._propertyDescriptor = propDecriptor;
                this._sortDirection = sortDirection;
            }

            public int Compare(T x, T y)
            {
                Type propertyType = this._propertyDescriptor.PropertyType;
                string propertyName = this._propertyDescriptor.Name;

                if (typeof(IComparable).IsAssignableFrom(propertyType))
                {
                    PropertyInfo pi = x.GetType().GetProperty(propertyName);
                    IComparable xComp = pi.GetValue(x, null) as IComparable;
                    IComparable yComp = pi.GetValue(y, null) as IComparable;

                    int orderMultiplier = (this._sortDirection == ListSortDirection.Ascending) ? 1 : -1;

                    if (xComp == null && yComp == null)
                        return 0;
                    else if (xComp == null)
                        return yComp.CompareTo(xComp) * orderMultiplier;
                    else
                        return xComp.CompareTo(yComp) * orderMultiplier;
                }
                else
                {
                    return 0;   //cannot sort so list will remain in the same order
                }
            }
        }

        #endregion
    }
}
